#region [Cursor Using Statements]
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using WP7_XNA_Accelerometer_Simulator.Utils.GameInfo;
#endregion

namespace WP7_XNA_Accelerometer_Simulator.Utils.Controls
{
    /// <summary>
    /// Cursor is a DrawableGameComponent that draws a cursor on the screen.
    /// </summary>
    public class Cursor : DrawableGameComponent
    {
        #region [Cursor Fields]
        // this constant controls how fast the gamepad moves the cursor. this constant
        // is in pixels per second.
        const float CursorSpeed = 400.0f;
        private Vector2 _position;
        #endregion

        #region [Cursor Properties]
        // this is the sprite that is drawn at the current cursor position.
        // textureCenter is used to center the sprite when drawing.
        Texture2D CursorTexture { get; set; }
        Vector2 TextureCenter { get; set; }
        
        public Vector2 Position
        {
            get { return _position; }
        }
        #endregion

        #region [Cursor Constructor]
        /// <summary>
        /// Cursor Constructor
        /// </summary>
        /// <param name="game"></param>
        public Cursor(Game game)
            : base(game)
        {
            LoadContent();
        }
        #endregion

        #region [Cursor LoadContent, Update and Draw Methods]
        // LoadContent needs to load the cursor texture and find its center.
        // also, we need to create a SpriteBatch.
        protected override void LoadContent()
        {
            CursorTexture = Game.Content.Load<Texture2D>(GameConstants.CursorUri);
            TextureCenter = new Vector2(CursorTexture.Width / 2, CursorTexture.Height / 2);

            
            // we want to default the cursor to start in the center of the screen
            Viewport vp = GameManager.GraphicsDeviceManager.GraphicsDevice.Viewport;
            _position.X = vp.X + (vp.Width / 2);
            _position.Y = vp.Y + (vp.Height / 2);

            base.LoadContent();
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            MouseState mouseState = Mouse.GetState();
            _position.X = mouseState.X;
            _position.Y = mouseState.Y;
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Draw(GameTime gameTime)
        {
            GameManager.SpriteBatch.Begin();

            // use textureCenter as the origin of the sprite, so that the cursor is 
            // drawn centered around Position.
            GameManager.SpriteBatch.Draw(CursorTexture, Position, null, Color.White, 0.0f,
                TextureCenter, 1.0f, SpriteEffects.None, 0.0f);

            GameManager.SpriteBatch.End();
        }
        #endregion

        #region CalculateCursorRay
        // CalculateCursorRay Calculates a world space ray starting at the camera's
        // "eye" and pointing in the direction of the cursor. Viewport.Unproject is used
        // to accomplish this. see the accompanying documentation for more explanation
        // of the math behind this function.
        public Ray CalculateCursorRay(Matrix projectionMatrix, Matrix viewMatrix)
        {
            // create 2 positions in screenspace using the cursor position. 0 is as
            // close as possible to the camera, 1 is as far away as possible.
            Vector3 nearSource = new Vector3(Position, 0f);
            Vector3 farSource = new Vector3(Position, 1f);

            // use Viewport.Unproject to tell what those two screen space positions
            // would be in world space. we'll need the projection matrix and view
            // matrix, which we have saved as member variables. We also need a world
            // matrix, which can just be identity.
            Vector3 nearPoint = GameManager.GraphicsDeviceManager.GraphicsDevice.Viewport.Unproject(nearSource,
                projectionMatrix, viewMatrix, Matrix.Identity);

            Vector3 farPoint = GameManager.GraphicsDeviceManager.GraphicsDevice.Viewport.Unproject(farSource,
                projectionMatrix, viewMatrix, Matrix.Identity);

            // find the direction vector that goes from the nearPoint to the farPoint
            // and normalize it....
            Vector3 direction = farPoint - nearPoint;
            direction.Normalize();

            // and then create a new ray using nearPoint as the source.
            return new Ray(nearPoint, direction);
        }

        #endregion
    }
}
